-
-
Notifications
You must be signed in to change notification settings - Fork 114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add timeouts to all wallet API calls #1722
Conversation
No dependency changes detected. Learn more about Socket for GitHub ↗︎ 👍 No dependency changes detected in pull request |
065459c
to
5619762
Compare
04fb7ad
to
96c2baa
Compare
if (err.name === 'AbortError') { | ||
// XXX node-fetch doesn't throw our custom TimeoutError but throws a generic error so we have to handle that manually. | ||
// see https://github.com/node-fetch/node-fetch/issues/1462 | ||
throw new FetchTimeoutError('POST', url, WALLET_CREATE_INVOICE_TIMEOUT_MS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this assumes the signal was a timeout signal that used WALLET_CREATE_INVOICE_TIMEOUT_MS
as the timeout which isn't clear from the immediate context but since we are smart and know more than just the immediate context and this is a workaround for node-fetch/node-fetch#1462, I think this is okay
? (data) => withTimeout( | ||
walletDef.testCreateInvoice(data, { | ||
logger, | ||
signal: timeoutSignal(WALLET_CREATE_INVOICE_TIMEOUT_MS) | ||
}), | ||
WALLET_CREATE_INVOICE_TIMEOUT_MS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mhhh, if the wallet can use signals, then it's not clear if withTimeout
or the signal will throw first 🤔
Preferably, we want the signal to throw first so we get FetchTimeoutError
as the error which includes additional information.
I tested this with LNbits and setting the timeout to 10 and the signal always won but not sure if we can rely on that. I'll add 100ms to withTimeout
to be sure but without affecting the timeout message itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in eff81eb
db5a174
to
eff81eb
Compare
Nice work. Tried with bunk nwc send/recv and it timeout appropriately. |
Description
This adds timeouts to all wallet API calls (
sendPayment
,testSendPayment
,testCreateInvoice
andcreateInvoice
). The timeouts are defined as constants (WALLET_CREATE_INVOICE_TIMEOUT_MS
andWALLET_SEND_PAYMENT_TIMEOUT_MS
). These timeouts fix the unresponsiveness as mentioned in #1558:#1558 only mentioned this for payments but I think we also want timeouts for creating invoices since they also affect responsiveness.
Regarding this:
This means
TimeoutError
needs to be considered aWalletPaymentError
for sender fallbacks (#1642) to work as expected. That is the case since we already throw any error as aWalletSenderError
inuseSendPayment
.Additionally to wrapping every wallet API call with
withTimeout
, we also pass anAbortSignal
which is passed to anyfetch
call inside the wallet API to abort any pending request on timeout. This is important to prevent any further logging after timeout. We need both (withTimeout
andAbortSignal
) because not every wallet usesfetch
but some use gRPC, websockets which don't have built-in support for signals.close #1558 based on #1724 #1725 #1726
TODO
Additional Context
AbortSignal
instance to all wallet API calls. It can then be passed to anyfetch
call so they also get aborted on timeout. Without this, requests will finish even though we don't need the response anymore. This has probably not a lot of impact on UX or performance but it just feels like a nice thing to do. It might not even be hard to do if it's really just passingsignal
tofetch
.update: yes, this is pretty important since else we will continue to log stuff inside the wallet API even though we already logged a timeout error. Added as TODO. The wallets that don't support signals don't log currently but if they ever do, we can also pass the signal to the logger calls to check if it's aborted.
I am also considering to make all API calls during
testSendPayment
andtestCreateInvoice
warnings in another PR so we don't need to worry too much about too short timeouts causing errors during attachment. Maybe it's also good if they are the same as the real timeouts since they give immediate feedback (as warnings) about the wallet response time ("health of the wallet")?I mentioned that we could use short invoice expiration in Timeout wallet payments #1558 (comment) but since the required cancel stuff was already handled in Sender fallbacks #1642, simply timing out the calls is enough.
If a receiver has multiple wallets, the timeout is currently applied to each wallet individually. This means that the full timeout to create an invoice can be multiples of
WALLET_CREATE_INVOICE_TIMEOUT_MS
. Maybe we should divide the timeout by the amount of wallets so each wallet gets the same time to create an invoice while still not waiting too long for an invoice from any wallet?node-fetch
doesn't throw the custom error we passed intocontroller.abort
1 so for CLN and LNbits, I didn't usefetchWithTimeout
but custom error handling. We usenode-fetch
because of the https agent support.Checklist
Are your changes backwards compatible? Please answer below:
yes
On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below:
8
. Tested by attaching every wallet (send+recv) except LNC and WebLN.Footnotes
https://github.com/node-fetch/node-fetch/issues/1462 ↩